package crmdna.client.venue;

import static crmdna.common.OfyService.ofy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Work;

import crmdna.client.group.Group;
import crmdna.client.sequence.Sequence;
import crmdna.client.sequence.Sequence.SequenceType;
import crmdna.client.user.User;
import crmdna.client.user.User.Action;
import crmdna.client.user.User.ResourceType;
import crmdna.common.APIException;
import crmdna.common.APIResponse.Status;
import crmdna.common.Utils;
import crmdna.crmdna.Client;

public class Venue {
	
	public static class VenueProp implements Comparable<VenueProp>{
		public long venueId;
		public String name;
		public String displayName;
		public String address;
		public long groupId;
		
		@Override
		public int compareTo(VenueProp arg0) {			
			return name.compareTo(arg0.name);
		}		
	}
		
	public static VenueProp create(String client, String displayName, String address, long groupId, 
			String login) {
		
		Client.ensureValidClient(client);
		User.ensureAccess(client, login, ResourceType.VENUE, 0, Action.WRITE);
		Group.safeGet(client, groupId);
		
		if ((null == displayName) || displayName.equals(""))
			Utils.throwIncorrectSpecException("display name is null or empty string");
		if ((null == address) || address.equals(""))
				Utils.throwIncorrectSpecException("address is null or empty string");
		
		String name = displayName.toLowerCase();
				
		List<Key<VenueEntity>> keys = ofy(client).load()
				.type(VenueEntity.class).filter("name", name).keys().list();
		
		if (keys.size() != 0)
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"There is already a venue with name [" + displayName + "]");
					
		String key = getUniqueKey(client, name);
		long val = MemcacheServiceFactory.getMemcacheService()
				.increment(key, 1, (long)0);
		
		if (val != 1)
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"There is already a venue with name [" + displayName + "]");
		
		VenueEntity entity = new VenueEntity();		
		entity.venueId = Sequence.getNext(client, SequenceType.VENUE);
		entity.name = name;
		entity.displayName = displayName;
		entity.address = address;
		entity.groupId = groupId;
		ofy(client).save().entity(entity).now();
		
		return entity.toProp();		
	}
	
	private static String getUniqueKey(String namespace, String name) {
		return namespace + "_" + SequenceType.VENUE + "_" + name;
 	}
			
	public static VenueEntity safeGet(String client, long venueId) {
				
		Client.ensureValidClient(client);
		
		VenueEntity entity = ofy(client).load().type(VenueEntity.class).id(venueId).get();
		if (null == entity)
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"Venue [" + venueId + "] does not exist");
		return entity;		
	}	
			
		
	static VenueProp update(final String client, final long venueId, final String newDisplayName, 
			final String newAddress, final Long newGroupId, String login) {
		
		Client.ensureValidClient(client);
		User.ensureAccess(client, login, ResourceType.VENUE, 0, Action.WRITE);
		
		if (newGroupId != null)
			Group.safeGet(client, newGroupId);
		
		ensureValid(newDisplayName, newAddress);
		
		VenueEntity entity = safeGet(client, venueId);
		
		if (newDisplayName != null) {			
			String newName = newDisplayName.toLowerCase();
							
			//if name is changing ensure it is unique
			if (! entity.name.equals(newName)) {
				List<Key<VenueEntity>> keys = ofy(client).load()
						.type(VenueEntity.class).filter("name", newName).keys().list();		
				if (keys.size() != 0)
					throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
							"There is already a venue with name [" + newDisplayName + "]");
				
				String key = getUniqueKey(client, newName);
				long val = MemcacheServiceFactory.getMemcacheService()
						.increment(key, 1, (long)0);
				
				if (val != 1)
					throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
							"There is already a venue with name [" + newDisplayName + "]");
			}						
		}
		
		VenueProp venueProp = ofy(client).transact(new Work<VenueProp>() {

			@Override
			public VenueProp run() {
				VenueEntity entity = safeGet(client, venueId);
				if (newDisplayName != null) {
					entity.displayName = newDisplayName;
					entity.name = newDisplayName.toLowerCase();
				}
				
				if (newAddress != null)
					entity.address = newAddress;
				if (newGroupId != null)
					entity.groupId = newGroupId;
				
				ofy(client).save().entity(entity).now();
				return entity.toProp();
			}
		});
		
		return venueProp;
	}
	
	private static void ensureValid(String displayName, String address) {
		if (displayName != null)
			if (displayName.equals(""))
				Utils.throwIncorrectSpecException("venue display name is empty string");
		
		if (address != null)
			if (address.equals(""))
				Utils.throwIncorrectSpecException("venue address is empty string");
	}
		
	static List<VenueProp> getAll(String client) {
		
		Client.ensureValidClient(client);
		
		List<VenueEntity> entities = ofy(client).load().type(VenueEntity.class).order("name").list();
		
		List<VenueProp> props = new ArrayList<>();
		for (VenueEntity entity : entities) 
			props.add(entity.toProp());
		
		Collections.sort(props);
		
		return props;						
	}
	
	static List<VenueProp> getAllForGroup(String client, long groupId) {
		Client.ensureValidClient(client);
		Group.safeGet(client, groupId);
		
		List<VenueEntity> entities = ofy(client).load().type(VenueEntity.class)
				.filter("groupId", groupId).list();
				
		List<VenueProp> props = new ArrayList<>();
		for (VenueEntity entity : entities) 
			props.add(entity.toProp());
		
		Collections.sort(props);
		
		return props;	
	}
			
	static void delete(String client, long venueId, String login) {
		Client.ensureValidClient(client);
		User.ensureAccess(client, login, ResourceType.VENUE, 0, Action.WRITE);
		
		//TODO - check other references
		throw new APIException(Status.ERROR_NOT_IMPLEMENTED, 
				"deleting venue not yet implemented");		
	}
}
